/******************************************************************************
 * @file     ram_marchcx_test_runtime.c
 * @version  V0.10
 * $Revision: 1 $
 * $Date: 21/02/23 9:23a $
 * @brief    IEC60730 MarchC method for testing RAM
 * @note
 * SPDX-License-Identifier: Apache-2.0
 * Copyright (C) 2016-2020 Nuvoton Technology Corp. All rights reserved.
 ******************************************************************************/

#include <string.h>
#include "nuc980.h"
#include "IEC60730_CONTROL_PARAM.h"
#include "IEC60730_SAFETY_TEST_LIB.h"

#ifdef __ARMCC_VERSION
extern uint32_t Image$$RW_STACK$$Base;
extern uint32_t Image$$RW_STACK$$ZI$$Length;

extern uint32_t Image$$RW_IRAM1$$Base;
extern uint32_t Image$$RW_IRAM1_END$$Base;
#endif

#ifdef __ICCARM__
extern uint32_t CSTACK$$Base;
extern uint32_t CSTACK$$Limit;

extern uint32_t RW_IRAM1$$Base;
extern uint32_t RW_IRAM1_END$$Base;
#endif

extern uint32_t __initial_sp;
static uint32_t s_u32RamCurrentAddr, s_u32g_u32RAMEndAddr;

extern uint32_t volatile g_u32RAMStartAddr;
extern uint32_t volatile g_u32RAMEndAddr;
extern uint32_t volatile g_u32RunTimeInit;

uint32_t volatile u32IntSetting;


__asm static void Disable_AllException(void)
{
    IMPORT  u32IntSetting

    push    {r0-r7}
    ldr     r1, =u32IntSetting
    mrs     r0, CPSR
    str     r0, [r1]
    orr     r0, r0, #0x000000C0 ;I-bit(7), F-bit(6) disabled
    msr     CPSR_c, r0
    mrs     r0, CPSR        ; check CPSR
    pop     {r0-r7}
    bx      lr              ; Branch back
}

__asm static void Enable_AllException(void)
{
    push    {r0-r7}
    ldr     r1, =u32IntSetting
    ldr     r0, [r1]
    msr     CPSR_c, r0      ; restore interrupt
    mrs     r0, CPSR
    pop     {r0-r7}
    bx      lr              ; Branch back
}

static void IEC60730_RAM_MarchCX_Test_RunTime_Init(void)
{
#ifdef __ICCARM__
    s_u32RamCurrentAddr = (uint32_t) &RW_IRAM1$$Base;
    s_u32g_u32RAMEndAddr = s_u32RamCurrentAddr + NUC980_SRAM_SIZE;
#endif

#ifdef __ARMCC_VERSION
    s_u32RamCurrentAddr = (uint32_t) &Image$$RW_IRAM1$$Base;
    s_u32g_u32RAMEndAddr = s_u32RamCurrentAddr + NUC980_SRAM_SIZE;
    s_u32RamCurrentAddr += 0x240;
#endif
}

//uint8_t IEC60730_RAM_MarchC_Test_RunTime(void)
uint8_t __IEC60730_RAM_MarchC_Test_RunTime(void)
{
    uint8_t u8RAMTestPass;
    uint8_t * pu8CopyToSafeArea;
    uint8_t *pu8AddrPtr;
    uint32_t u32StartAddr, u32EndAddr;
    uint32_t u32Length = RAM_RUNTIME_TEST_LENGTH;
    volatile uint32_t testFlag = 0;;

    if (!(g_u32RunTimeInit & RUNTIME_RAM_INIT))
    {
        g_u32RunTimeInit |= RUNTIME_RAM_INIT;
        IEC60730_RAM_MarchCX_Test_RunTime_Init();
        u8RAMTestPass = TEST_PASS;
    }

    u32StartAddr = s_u32RamCurrentAddr;
    u32EndAddr = s_u32g_u32RAMEndAddr;
    pu8AddrPtr = (uint8_t *) u32StartAddr;

    /* disable interrupt for safe */
    Disable_AllException();

    /* if we need to backup ram data, safe ram reserved area is
       [0x3C004000-0x10*2  0x3C003FFF] */
    if (u32StartAddr == u32EndAddr - u32Length*2)
        pu8CopyToSafeArea= (uint8_t*) (u32EndAddr - u32Length);
    else
        pu8CopyToSafeArea= (uint8_t*) (u32EndAddr - u32Length*2);

    memcpy(pu8CopyToSafeArea, pu8AddrPtr, u32Length);

    /* start ram test */
    g_u32RAMStartAddr = u32StartAddr;
    g_u32RAMEndAddr =  g_u32RAMStartAddr + u32Length;
    g_u32RAMEndAddr -= 1;

#if 0
    printf("g_u32RAMStartAddr = 0x%x, g_u32RAMEndAddr = 0x%x !!! \n", g_u32RAMStartAddr, g_u32RAMEndAddr);
    printf("pu8CopyToSafeArea = 0x%x !!! \n", (uint32_t) pu8CopyToSafeArea);
//  if (g_u32RAMStartAddr==0x3c0000b0)
//      testFlag = 1;
#endif

    u8RAMTestPass = IEC60730_PartRamMarchC_WOM();

    /* restore the data */
    if(pu8CopyToSafeArea)
        memcpy(pu8AddrPtr, pu8CopyToSafeArea, u32Length);

    /* check if all ram have been tested (excluding safe reserved area) */
    u32StartAddr += u32Length;
    if (u32StartAddr >= u32EndAddr)
        g_u32RunTimeInit &= ~RUNTIME_RAM_INIT;

    /* based on Artisan's SRAM architecture, for overlap test */
    u32StartAddr -= u32Length/2;
    s_u32RamCurrentAddr = u32StartAddr;

    /* enable interrupt */
    Enable_AllException();
    return u8RAMTestPass;
}

uint8_t IEC60730_RAM_MarchX_Test_RunTime(void)
{
    uint8_t u8RAMTestPass;
    uint8_t * pu8CopyToSafeArea;
    uint8_t *pu8AddrPtr;
    uint32_t u32StartAddr, u32EndAddr;
    uint32_t u32Length = RAM_RUNTIME_TEST_LENGTH;

    if (!(g_u32RunTimeInit & RUNTIME_RAM_INIT))
    {
        g_u32RunTimeInit |= RUNTIME_RAM_INIT;
        IEC60730_RAM_MarchCX_Test_RunTime_Init();
        u8RAMTestPass = TEST_PASS;
    }

    u32StartAddr = s_u32RamCurrentAddr;
    u32EndAddr = s_u32g_u32RAMEndAddr;
    pu8AddrPtr = (uint8_t *) u32StartAddr;

    /* disable interrupt for safe */
    Disable_AllException();

    /* if we need to backup ram data, safe ram reserved area is
       [0x200008000-0x10*2  0x200007FFF] */
    if (u32StartAddr == u32EndAddr - u32Length*2)
        pu8CopyToSafeArea= (uint8_t*) (u32EndAddr - u32Length);
    else
        pu8CopyToSafeArea= (uint8_t*) (u32EndAddr - u32Length*2);

    memcpy(pu8CopyToSafeArea, pu8AddrPtr, u32Length);

    /* start ram test */
    g_u32RAMStartAddr = u32StartAddr;
    g_u32RAMEndAddr =  g_u32RAMStartAddr + u32Length;
    g_u32RAMEndAddr -= 1;

    u8RAMTestPass = IEC60730_PartRamMarchX_WOM();

    /* restore the data */
    if(pu8CopyToSafeArea)
        memcpy(pu8AddrPtr, pu8CopyToSafeArea, u32Length);

    /* check if all ram have been tested (excluding safe reserved area) */
    u32StartAddr += u32Length;
    if (u32StartAddr >= u32EndAddr)
        g_u32RunTimeInit &= ~RUNTIME_RAM_INIT;

    /* based on Artisan's SRAM architecture, for overlap test */
    u32StartAddr -= u32Length/2;
    s_u32RamCurrentAddr = u32StartAddr;

    /* enable interrupt */
    Enable_AllException();
    return u8RAMTestPass;
}

